package DB.Connection;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class DBConnectionPool {

	private static final Log logger = LogFactory.getLog(DBConnectionPool.class);

	private static List<Connection> connectionPool = new LinkedList<Connection>();
	protected static final ReentrantLock lock = new ReentrantLock();

	private static boolean FirstConnection = true;
	private static boolean SetParams = false;

	/* Connection parameters */
	private static String url;
	private static String username;
	private static String password;

	private static int nPassedConnections = 0;

	/**
	 * This method MUST BE CALLED before any connections can be created
	 * 
	 * @param connUrl
	 * @param user
	 * @param pass
	 */
	public static void SetConnectionParams(String connUrl, String user, String pass) {
		url = connUrl;
		username = user;
		password = pass;

		SetParams = true;
	}

	/**
	 * Get a connection from the pool If no connections available, create a new
	 * one and add to pool
	 * 
	 * @return connection
	 */
	public static Connection getConnection() {

		// Connection Params must be set before using the pool
		if (!SetParams) {
			logger.error("DBConnectionPool::getConnection: ERROR - connection parameter haven't been set yet");
			return null;
		}
		try {
			lock.lock();
			// loading the driver in first connection
			if (FirstConnection) {

				try {
					Class.forName("com.mysql.jdbc.Driver");
				} catch (ClassNotFoundException e) {
					logger.error("DBConnectionPool::getConnection: ERROR - Unable to load the mysql JDBC driver", e);
					return null;
				}
				FirstConnection = false;
			}

			// No connections ? create a new one and add to pool
			if (connectionPool.isEmpty()) {
				try {
					Connection conn = DriverManager.getConnection(url, username, password);
					if (conn == null) {
						throw new SQLException();
					}
					connectionPool.add(conn);
				} catch (SQLException e) {
					logger.error("DBConnectionPool::getConnection: ERROR - Unable to connect - ", e);
					return null;
				}
			}

			// Now list is not empty, return one of the connections
			nPassedConnections++;
			return (connectionPool.remove(0));
		} finally {
			lock.unlock();
		}
	}

	/**
	 * Returns a connection back to the pool
	 * 
	 * @precondition: The connection was retrieved from the pool
	 * @param conn
	 */
	public static void endConnection(Connection conn) {

		try {
			lock.lock();
			nPassedConnections--;
			connectionPool.add(conn);
		} finally {
			lock.unlock();
		}
	}

	/**
	 * Close all connections
	 * 
	 * @precondition: All distributed connections have returned to the pool (or
	 *                else, they won't be closed)
	 */
	public static void closeAllConnections() {
		try {
			lock.lock();
			for (Connection conn : connectionPool) {
				try {
					conn.close();
				} catch (SQLException e) {
					logger.error("DBConnectionPool::closeAllConnections: ERROR - exception when closing connection: " + e.toString());
				}
			}
			logger.info("DBConnectionPool::closeAllConnections [INFO]: The number of requested and not returned connections is: " + nPassedConnections);
		} finally {
			lock.unlock();
			logger.info("DBConnectionPool::closeAllConnections: INFO - closed all connections");
		}
	}
}
